﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Web;
using Cms.Application.IO;
using Cms.Data;
using Cms.Domain;
using Cms.Domain.RepositoryInterfaces;
using Cms.Utilities;
using iLib.Web.UI;
using Ionic.Zip;
using Ionic.Zlib;

namespace Cms.Application
{
    public class DiskAccess : IDiskAccess
    {
        #region IDiskAccess Members

        public void CreateFolder(string path)
        {
            if (HttpContext.Current == null)
                return;
            string physicalPath = Url.PhysicalPath + path.Replace("/", "\\");
            if (!Directory.Exists(physicalPath))
                Directory.CreateDirectory(physicalPath);
            if (File.Exists(physicalPath + "\\default.aspx"))
                return;
            StreamWriter sw = File.CreateText(physicalPath + "\\default.aspx");
            sw.Close();
            sw.Dispose();
            Logging.LogInfo("DiskAccess.CreateFolder: " + path);
        }
        
        public void RecreateAllDirectories()
        {
            if (HttpContext.Current == null)
                return;
            try
            {
                IRepositoryFactory rf = new RepositoryFactory();
                foreach (Site site in rf.SiteRepository.GetSites())
                {
                    // Remove all folders
                    foreach (Folder folder in site.GetRootFolders())
                    {
                        string path = Url.PhysicalPath + folder.GetVirtualPath().Replace("/", "\\");
                        if (Directory.Exists(path))
                            Directory.Delete(path, true);
                    }
                    // Recreate them
                    foreach (Folder folder in site.Folders)
                    {
                        CreateFolder(folder.GetVirtualPath());
                    }
                }
                Logging.LogInfo("RecreateAllDirectories succeeded");
            }
            catch (ApplicationException ex)
            {
                Logging.LogError("RecreateAllDirectories error: ", ex);
                throw;
            }
        }
        
        public void MoveFolder(string sourcePath, string destPath)
        {
            if (HttpContext.Current == null)
                return;
            try
            {
                iLib.Utils.IO.MoveDirectory(Url.PhysicalPath + sourcePath.Replace("/", "\\"),
                                            Url.PhysicalPath + destPath.Replace("/", "\\"));
                Logging.LogInfo("DiskAccess.MoveFolder: " + sourcePath + ", " + destPath);
            }
            catch (Exception ex)
            {
                Logging.LogError("Could not move \"" + Url.PhysicalPath + sourcePath.Replace("/", "\\") +
                                 "\" to \"" + Url.PhysicalPath + destPath.Replace("/", "\\") + "\"", ex);
                throw;
            }
            finally
            {
                RecreateAllDirectories();
            }
        }
        
        public void CreateDirectory(string path)
        {
            if (!Directory.Exists(Url.PhysicalPath + path))
                Directory.CreateDirectory(Url.PhysicalPath + path);
            Logging.LogInfo("DiskAccess.CreateDirectory: " + path);
        }
        
        public void DeleteDirectory(string path)
        {
            if (Directory.Exists(Url.PhysicalPath + path))
                Directory.Delete(Url.PhysicalPath + path, true);
            Logging.LogInfo("DiskAccess.DeleteDirectory: " + path);
        }

        public void ClearDirectory(string path)
        {
            if (!Directory.Exists(Url.PhysicalPath + path))
            {
                CreateDirectory(path);
                return;
            }
            foreach (string subdir in Directory.GetDirectories(Url.PhysicalPath + path))
                Directory.Delete(subdir, true);
            foreach (string file in Directory.GetFiles(Url.PhysicalPath + path))
                File.Delete(file);
            Logging.LogInfo("DiskAccess.ClearDirectory: " + path);
        }
        
        public void DeleteFolder(string path)
        {
            if (HttpContext.Current == null)
                return;
            string physicalPath = Url.PhysicalPath + path.Replace("/", "\\");
            if (Directory.Exists(physicalPath))
                Directory.Delete(physicalPath, true);
            Logging.LogInfo("DiskAccess.DeleteFolder: " + path);
            RecreateAllDirectories();
        }
        
        public void DeleteFile(string path)
        {
            if (File.Exists(Url.PhysicalPath + path))
                File.Delete(Url.PhysicalPath + path);
            Logging.LogInfo("DiskAccess.DeleteFile: " + path);
        }

        public void MoveFile(string oldPath, string newPath)
        {
            if (File.Exists(Url.PhysicalPath + oldPath) && oldPath.ToLower() != newPath.ToLower())
                File.Move(Url.PhysicalPath + oldPath, Url.PhysicalPath + newPath);
            Logging.LogInfo("DiskAccess.MoveFile: " + oldPath + ", " + newPath);
        }
        
        public void CopyFile(string oldPath, string newPath)
        {
            if (File.Exists(Url.PhysicalPath + oldPath))
                File.Copy(Url.PhysicalPath + oldPath, Url.PhysicalPath + newPath);
            Logging.LogInfo("DiskAccess.CopyFile: " + oldPath + ", " + newPath);
        }
        
        public void MoveDirectory(string oldPath, string newPath)
        {
            if (Directory.Exists(Url.PhysicalPath + oldPath) && oldPath.ToLower() != newPath.ToLower())
                iLib.Utils.IO.MoveDirectory(Url.PhysicalPath + oldPath, Url.PhysicalPath + newPath);
            Logging.LogInfo("DiskAccess.MoveDirectory: " + oldPath + ", " + newPath);
        }
        
        public void CreateTextFile(string path, string code)
        {
            StreamWriter sw = File.CreateText(Url.PhysicalPath + path);
            sw.Write(code);
            sw.Close();
            sw.Dispose();
            Logging.LogInfo("DiskAccess.CreateTextFile: " + path + ", code:" + Environment.NewLine + code + Environment.NewLine);
        }
        
        public string ReadTextFile(string path)
        {
            string content = "";
            if (File.Exists(Url.PhysicalPath + path))
            {
                StreamReader sr = File.OpenText(Url.PhysicalPath + path);
                content = sr.ReadToEnd();
                sr.Close();
                sr.Dispose();
            }
            Logging.LogInfo("DiskAccess.ReadTextFile: " + path);
            return content;
        }

        public void Compress(IList<MediaFile> selectedFiles, string zipFilename)
        {
            using (var zip = new ZipFile())
            {
                zip.CompressionLevel = CompressionLevel.BestCompression;
                foreach (MediaFile file in selectedFiles)
                {
                    ZipEntry entry = zip.AddFile(Url.PhysicalPath + file.GetPhysicalPath(), "");
                    entry.FileName = file.Name;
                }
                zip.Save(Url.PhysicalPath + zipFilename);
            }
            Logging.LogInfo("DiskAccess.Compress: " + zipFilename);
        }
        
        public IList<MediaFile> Decompress(MediaFile zippedMediaFile)
        {
            using (ZipFile zip = ZipFile.Read(Url.PhysicalPath + zippedMediaFile.GetPhysicalPath()))
            {
                string existingFiles = "";
                foreach (string file in zip.EntryFileNames)
                {
                    if (
                        File.Exists(Url.PhysicalPath +
                                    zippedMediaFile.GetPhysicalPath().Replace(zippedMediaFile.Name, "") + file))
                    {
                        if (existingFiles != "")
                            existingFiles += ", ";
                        existingFiles += file;
                    }
                }
                if (existingFiles != "")
                    throw new IOException(existingFiles);
                IList<MediaFile> files = new List<MediaFile>();
                foreach (string file in zip.EntryFileNames)
                {
                    var mf = new MediaFile();
                    mf.Name = file;
                    mf.Folder = zippedMediaFile.Folder;
                    if (mf.Folder != null)
                        mf.Folder.Files.Add(mf);
                    mf.Extension = file.Substring(file.LastIndexOf(".") + 1);
                    mf.Size = file.Length;
                    mf.UploadedTimestamp = DateTime.Now;
                    mf.Site = zippedMediaFile.Site;
                    mf.Site.MediaFiles.Add(mf);
                    files.Add(mf);
                }
                foreach (ZipEntry entry in zip)
                {
                    entry.Extract(Url.PhysicalPath + zippedMediaFile.GetPhysicalPath().Replace(zippedMediaFile.Name, ""));
                }
                Logging.LogInfo("DiskAccess.Decompress: " + zippedMediaFile);
                return files;
            }
        }

        public ICmsFileInfo GetFileInfo(string path)
        {
            return new CmsFileInfo(path);   
        }

        #endregion
    }
}